
function solveNishioStepper()
{
	if (this.strategySingles())
	{
		return true;
	}

	// Basic Strategies
	if (this.strategyHiddenSingles())
	{
		return true;
	}
	if (this.strategyLockedCandidates())
	{
		return true;
	}

	// Subset Strategies
	if (this.strategyNakedSubset())
	{
		return true;
	}
	if (this.strategyHiddenSubset())
	{
		return true;
	}
	if (this.strategyFish())
	{
		return true;
	}

	if (this.strategy3DMedusa())
	{
		return true;
	}

	return false;
}

// Backtracking Nishio
SudokuGrid.prototype.strategyNishio = function (CellList, position)
{
	if (this.solveLoop(solveNishioStepper))
	{
		SudokuUI.log.print('SOLUTION!!!');
		return true;
	}

	if (typeof position !== 'number') var position = 0;
	var s= 'R' + position + ': ';

	if (typeof CellList === 'object')
	{
		// compile a list of candidate cells candidate
		var BitCount, CellList = [];
		for (var r = 0; r < 9; r++)
		{
			for (var c = 0; c < 9; c++)
			{
				BitCount = this.notations[r][c].bitCount();
				if (BitCount > 1) CellList.push( [ r, c, BitCount ] );
			}
		}
		CellList.sort(function (a, b) {return (a[2] - b[2]);});
	}

	do
	{
		var row = CellList[position][0];
		var col = CellList[position][1];
		position++;
	} while (this.notations[row][col].bitCount() === 1);
	

	if (CellList.length === position)
	{
		SudokuUI.log.print(s + 'Backtrack (' + row + ',' + col + ').');
		return false;
	}

	console.log(s + 'Nishio from (' + row + ', ' + col + ')');
	var candidates = this.notations[row][col].splitBinary();
	var trial;

	// loop thru candidates
	for (var i = 0, len = candidates.length; i < len; i++)
	{
		trial = new SudokuGrid(this);
		SudokuUI.log.print(s + 'Try ' + candidates[i].bitmaskToDigit() + ' of ' + this.notations[row][col].bitmaskToString() + ' (' + row + ',' + col + ')');
		trial.setNotation(row, col, candidates[i]);

		try
		{
			var returnValue = trial.strategyNishio(CellList, position);
		}
		catch (e)
		{
			continue;
		}

		// if no success, continue to next condidate.
		if (!returnValue) continue;

		console.log('Nishio: setting cell (' + row + ',' + col + ') to ' + candidates[i].bitmaskToDigit() + ' leads to solution.');

		for (var r = 0; r < 9; r++)
			for (var c = 0; c < 9; c++)
			{
				this.setNotation(r, c, trial.notations[r][c]);
			}

			this.strategySingles();

		SudokuUI.log.print(s + '(' + row + ',' + col + ') as ' + candidates[i].bitmaskToDigit() + ': Sucess!');
		return true;
	}

	// all candidates exhausted
	SudokuUI.log.print(s + 'Backtrack (' + row + ',' + col + ').');
	return false;
}

SudokuGrid.prototype.strategyTemplating_Recurse = function (array, reconstruct, prefix, depth, boxconstraint)
{
	if (depth % 3 === 0) boxconstraint = 511;

	// get candidate, also mask out any value that's already picked
	var CandidateList = (array[depth] & ~prefix & boxconstraint).splitBinary();
	var valid = false;
	var NextLayer = depth + 1;

	for (var i = 0, len = CandidateList.length; i < len; i++)
	{
		var constraint = 7;
		while ((CandidateList[i] & constraint) === 0) constraint <<= 3;

		// check if we reached the end of the list, this path must be valid
		if (NextLayer === array.length || strategyTemplating_Recurse (array, reconstruct, prefix | CandidateList[i], NextLayer, boxconstraint & ~constraint))
		{
			reconstruct[depth] |= CandidateList[i];
			valid = true;
		}
	}
	return valid;
}

SudokuGrid.prototype.strategyTemplating = function ()
{
	console.log('=== Templating ===');
	var oldA = [], newA;
	var changed = false;

	for (var v = 1; v < 512; v <<= 1)
	{
		newA = [0, 0, 0, 0, 0, 0, 0, 0, 0];
		for (var i = 0; i < 9; i++)
			oldA[i] = this.getPositionsInCol(v, i);

		strategyTemplating_Recurse(oldA, newA, 0, 0);

		if (oldA.toString() !== newA.toString())
		{
			console.log(oldA);
			console.log(newA);
		}

		for (var i = 0; i < 9; i++)
		{
			if (oldA[i] !== newA[i])
			{
				console.log(i + ': ' + oldA[i] + '&rarr;' + newA[i]);
				this.setPositionsInCol(v, i, newA[i]);
				changed = true;
			}
		}

		newA = [0, 0, 0, 0, 0, 0, 0, 0, 0];
		for (var i = 0; i < 9; i++)
			oldA[i] = this.getPositionsInRow(v, i);

		strategyTemplating_Recurse(oldA, newA, 0, 0);

		if (oldA.toString() !== newA.toString())
		{
			console.log(oldA);
			console.log(newA);
		}

		for (var i = 0; i < 9; i++)
		{
			if (oldA[i] !== newA[i])
			{
				console.log(i + ': ' + oldA[i] + '&rarr;' + newA[i]);
				this.setPositionsInRow(v, i, newA[i]);
				changed = true;
			}
			
		}

	}
	return changed;
}
